#!/usr/bin/env python3
"""
GitExecutor Git 命令执行器
安全执行 Git 命令，提供超时控制和错误处理
"""

import subprocess
import os
import shlex
import signal
from typing import Optional, Tuple, List
from models.git_repository import GitRepository


class GitExecutor:
    """Git 命令执行器"""
    
    def __init__(self, timeout: int = 300, verbose: bool = False):
        """
        初始化 Git 执行器
        
        Args:
            timeout: 命令执行超时时间（秒）
            verbose: 是否启用详细输出
        """
        self.timeout = timeout
        self.verbose = verbose
    
    def execute_git_command(self, repo_path: str, command: List[str]) -> Tuple[bool, str, str]:
        """
        安全执行 Git 命令
        
        Args:
            repo_path: 仓库路径
            command: Git 命令列表（如 ["pull", "--ff-only"]）
            
        Returns:
            Tuple[bool, str, str]: (成功状态, 标准输出, 错误输出)
        """
        try:
            # 验证仓库路径
            if not self._validate_repo_path(repo_path):
                return False, "", f"无效的仓库路径: {repo_path}"
            
            # 构建完整的 Git 命令
            full_command = ["git"] + command
            
            # 安全检查：确保命令只包含 Git 相关操作
            if not self._is_safe_git_command(command):
                return False, "", f"不安全的 Git 命令: {command}"
            
            # 在仓库目录中执行命令
            if self.verbose:
                print(f"执行命令: {' '.join(full_command)} in {repo_path}")
            
            # 执行命令
            result = subprocess.run(
                full_command,
                cwd=repo_path,
                capture_output=True,
                text=True,
                timeout=self.timeout,
                preexec_fn=lambda: os.setsid()  # 创建新的进程组，便于信号处理
            )
            
            # 检查执行结果
            if result.returncode == 0:
                if self.verbose:
                    print(f"命令执行成功: {result.stdout}")
                return True, result.stdout, ""
            else:
                error_msg = result.stderr or result.stdout
                if self.verbose:
                    print(f"命令执行失败: {error_msg}")
                return False, "", error_msg
                
        except subprocess.TimeoutExpired:
            error_msg = f"命令执行超时（{self.timeout}秒）: git {' '.join(command)}"
            if self.verbose:
                print(error_msg)
            return False, "", error_msg
            
        except Exception as e:
            error_msg = f"执行 Git 命令时发生错误: {str(e)}"
            if self.verbose:
                print(error_msg)
            return False, "", error_msg
    
    def pull_repository(self, repo_path: str, remote: str = "origin", branch: str = "main") -> Tuple[bool, str, str]:
        """
        执行 git pull 操作
        
        Args:
            repo_path: 仓库路径
            remote: 远程仓库名称
            branch: 分支名称
            
        Returns:
            Tuple[bool, str, str]: (成功状态, 标准输出, 错误输出)
        """
        try:
            if self.verbose:
                print(f"正在更新仓库: {repo_path}")
            
            # 检查是否为 Git 仓库
            if not self._validate_repo_path(repo_path):
                return False, "", f"无效的 Git 仓库: {repo_path}"
            
            # 执行 git pull
            command = ["pull", remote, branch]
            success, stdout, stderr = self.execute_git_command(repo_path, command)
            
            if success:
                if self.verbose:
                    print(f"仓库 {repo_path} 更新成功")
                return True, stdout, stderr
            else:
                if self.verbose:
                    print(f"仓库 {repo_path} 更新失败: {stderr}")
                return False, stdout, stderr
                
        except Exception as e:
            error_msg = f"更新仓库时发生异常: {str(e)}"
            if self.verbose:
                print(error_msg)
            return False, "", error_msg
    
    def get_repository_status(self, repo_path: str) -> Tuple[bool, str, str]:
        """
        获取仓库状态
        
        Args:
            repo_path: 仓库路径
            
        Returns:
            Tuple[bool, str, str]: (成功状态, 状态信息, 错误信息)
        """
        try:
            if not self._validate_repo_path(repo_path):
                return False, "", f"无效的 Git 仓库: {repo_path}"
            
            command = ["status", "--porcelain"]
            return self.execute_git_command(repo_path, command)
            
        except Exception as e:
            return False, "", str(e)
    
    def _validate_repo_path(self, repo_path: str) -> bool:
        """
        验证仓库路径的有效性
        
        Args:
            repo_path: 仓库路径
            
        Returns:
            bool: 路径是否有效
        """
        try:
            import pathlib
            
            # 检查路径是否存在
            path = pathlib.Path(repo_path)
            if not path.exists():
                return False
            
            # 检查是否为目录
            if not path.is_dir():
                return False
            
            # 检查是否为 Git 仓库
            git_dir = path / ".git"
            if not git_dir.exists():
                return False
            
            # 安全检查：防止路径遍历攻击
            real_path = os.path.realpath(repo_path)
            current_path = os.path.realpath(os.getcwd())
            if not real_path.startswith(current_path):
                return False
            
            return True
            
        except Exception:
            return False
    
    def _is_safe_git_command(self, command: List[str]) -> bool:
        """
        检查 Git 命令是否安全
        
        Args:
            command: Git 命令列表
            
        Returns:
            bool: 命令是否安全
        """
        dangerous_commands = [
            "rm", "mv", "del", "delete",
            "reset", "clean", "fsck",
            "push", "remote", "branch",
            "config", "clone", "init"
        ]
        
        # 检查是否包含危险命令
        if command and command[0] in dangerous_commands:
            return False
        
        # 检查是否包含重定向操作
        dangerous_chars = ['>', '<', '|', '&', ';', '`', '$(', '${']
        for cmd in command:
            if any(char in cmd for char in dangerous_chars):
                return False
        
        return True


def test_git_executor():
    """测试 Git 执行器"""
    print("测试 Git 执行器...")
    
    executor = GitExecutor(timeout=30, verbose=True)
    
    # 测试当前目录（如果存在）
    current_dir = "."
    
    # 验证仓库路径
    is_valid = executor._validate_repo_path(current_dir)
    print(f"当前目录是否为有效 Git 仓库: {is_valid}")
    
    if is_valid:
        # 测试 git status 命令
        print("测试 git status 命令...")
        success, stdout, stderr = executor.execute_git_command(current_dir, ["status"])
        print(f"状态检查结果: 成功={success}, 输出={stdout}, 错误={stderr}")
    
    # 测试安全检查
    print("测试安全检查...")
    safe_command = ["status"]
    unsafe_command = ["rm", "-rf", "/"]
    
    print(f"安全命令检查: {safe_command} -> {executor._is_safe_git_command(safe_command)}")
    print(f"危险命令检查: {unsafe_command} -> {executor._is_safe_git_command(unsafe_command)}")
    
    # 测试无效路径
    print("测试无效路径...")
    success, stdout, stderr = executor.pull_repository("/nonexistent/path")
    print(f"无效路径测试结果: 成功={success}, 错误={stderr}")


if __name__ == "__main__":
    test_git_executor()